package com.projetTDA.metier;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;

//Constructeur tournoi

public class Tournoi {


	private int id_tournoi;
	private String nom_tournoi;
	private String date_creation;
	private String lib_sport;
	private LinkedHashMap<Equipe,Integer> classement;
	private ArrayList<Match> liste_matchs;
	private boolean isModeChampionnat;


	public boolean isModeChampionnat() {
		return isModeChampionnat;
	}


	@Deprecated
	public Tournoi(int id_tournoi, String nom_tournoi, String date_creation, String lib_sport, Boolean is_championnat,
			HashMap<Equipe, Integer> classement, ArrayList<Match> liste_matchs) {
		super();
		this.id_tournoi = id_tournoi;
		this.nom_tournoi = nom_tournoi;
		this.date_creation = date_creation;
		this.lib_sport = lib_sport;
		this.isModeChampionnat = is_championnat;
		this.classement = new LinkedHashMap<Equipe, Integer>(classement);
		this.liste_matchs = new ArrayList<Match>(liste_matchs);
	}


	public Tournoi(String p_nomTournoi, String p_dateCreation, String p_libelleSport,
			LinkedHashMap<Equipe,Integer> p_classement, boolean p_isChampionnat) {
		super();
		this.nom_tournoi = p_nomTournoi;
		this.date_creation = p_dateCreation;
		this.lib_sport = p_libelleSport;
		this.classement = p_classement;
		this.isModeChampionnat = p_isChampionnat;
		this.liste_matchs = new ArrayList<Match>();
		this.id_tournoi = -1;

		//Dans le cas d'un championnat, les ≈Ωquipes se battent les unes contre les autres
		if (this.isChampionnat()) {
			System.out.println("on part ds le mode championnat");
			genererMatchsModeChampionnat();
		}
		else { //Sinon, dans le cas d'un tournoi ≈Ωliminatoire, on tire au sort les matchs
			System.out.println("on part ds le mode éliminatoire");
			tirageAuSortMatchEliminatoire();
		}
	}

	@Deprecated
	public Tournoi() {
		super();
		this.liste_matchs=new ArrayList<Match>();
		this.classement = new LinkedHashMap<Equipe,Integer>();
		this.isModeChampionnat = false;
	}

	public int getId_tournoi() {
		return id_tournoi;
	}

	public void setId_tournoi(int id_tournoi) {
		this.id_tournoi = id_tournoi;
	}

	public ArrayList<Match> getListe_matchs() {
		return liste_matchs;
	}

	public void setListe_matchs(ArrayList<Match> p_liste_matchs) {
		this.liste_matchs = p_liste_matchs;
	}

	public boolean isChampionnat() {
		return isModeChampionnat;
	}

	public void setChampionnat(boolean isChampionnat) {
		this.isModeChampionnat = isChampionnat;
	}

	public String getNom_tournoi() {
		return nom_tournoi;
	}

	public void setNom_tournoi(String nom_tournoi) {
		this.nom_tournoi = nom_tournoi;
	}

	public String getDate_creation() {
		return date_creation;
	}

	public void setDate_creation(String date_creation) {
		this.date_creation = date_creation;
	}

	public String getLib_sport() {
		return lib_sport;
	}

	public void setLib_sport(String lib_sport) {
		this.lib_sport = lib_sport;
	}

	public HashMap<Equipe,Integer> getClassement() {

		return classement;
	}

	public void setClassement(LinkedHashMap<Equipe,Integer> p_classement) {
		this.classement = p_classement;
	}

	@Override
	public String toString() {

		StringBuilder sb = new StringBuilder();

		//sb.append("|");
		sb.append(this.nom_tournoi);
		sb.append("\n");
		sb.append("Mode Championnat : " + this.isModeChampionnat);
		sb.append("\n");
		sb.append(this.date_creation);
		sb.append("\n");
		sb.append(this.lib_sport);
		sb.append("\n");
		sb.append("\n");
		sb.append("Classement:");
		sb.append("\n");
		sb.append(this.classement);
		sb.append("\n");
		sb.append("\n");
		sb.append("Matchs:");
		sb.append("\n");
		sb.append(this.liste_matchs);


		return sb.toString();	
	}

	/**
	 * Fonction qui trie la HashMap (classement) selon la valeur (le socre).
	 */
	public void trierClassement()
	{
		Equipe[] listeEquipe = Arrays.copyOf(classement.keySet().toArray(), classement.keySet().size(), Equipe[].class);
		Integer[] listeScore = Arrays.copyOf(classement.values().toArray(), classement.values().size(), Integer[].class);

		int longueur = listeScore.length;
		Integer tmp = Integer.valueOf(0);
		Equipe tmpTmp;
		Boolean permut;

		do
		{			
			permut = false;

			for(int i = 0; i < longueur - 1; i++)
			{
				if(listeScore[i] < listeScore[i + 1])
				{
					tmp = listeScore[i];
					listeScore[i] = listeScore[i + 1];
					listeScore[i + 1] = tmp;

					tmpTmp = listeEquipe[i];
					listeEquipe[i] = listeEquipe[i + 1];
					listeEquipe[i + 1] = tmpTmp;

					permut = true;
				}
			}			
		}while(permut);

		classement.clear();

		for(int i = 0; i < longueur; i++)
		{
			classement.put(listeEquipe[i], listeScore[i]);
		}
	}

	public void ajouterEquipe(Equipe p_equipeTOAdd) {
		boolean trouve = false;
		String nomEquipeDoublon ="";
		if (classement.size() != 0) {
			for (Entry<Equipe, Integer> entry: classement.entrySet() ) {
				//Test pour ≈Ωviter de rajouter deux noms identiques ou deux id identiques : ex on ne peut pas ajouter deux fois le br≈Ωsil
				if (p_equipeTOAdd.getId_equipe() == entry.getKey().getId_equipe() || p_equipeTOAdd.getNom_equipe() == entry.getKey().getNom_equipe()) {
					trouve = true;
					nomEquipeDoublon= entry.getKey().getNom_equipe();
					break;
				}
			}
		}

		if(!trouve) {
			if (!classement.containsKey(p_equipeTOAdd)) {

				classement.put(p_equipeTOAdd, null);
			}
			else {
				System.out.println("Equipe {" + p_equipeTOAdd.getNom_equipe() + "(" + p_equipeTOAdd.getId_equipe()+ ")} deja enregistree pour ce tournoi, cette entree ne sera pas memorise.");
			}
		}
		else {
			System.out.println("Une equipe possedant l'identifiant que vous essayez d'inserer existe deja pour ce tournoi (" + nomEquipeDoublon + " avec l'identifiant " + p_equipeTOAdd.getId_equipe() + "), cette entree ne sera pas memorisee.");
		}
	}


	public void tirageAuSortMatchEliminatoire() {
		System.out.println("<-----  Debut du tirage au sort du rang 1 ----->");

		int nbEquipes = classement.size();
		int cpt = 0;
		System.out.println("          Nombre de participants : " + nbEquipes);

		try {
			if(nbEquipes > 1) {  // on fait les tirages
				if (nbEquipes == 2 || nbEquipes == 4 || nbEquipes == 8) {
					ArrayList<Integer> iDsEquipes = new ArrayList<Integer>();

					//On recupere les ids de toutes les √É¬©quipes
					for (Entry<Equipe, Integer> entry: classement.entrySet() ) {
						iDsEquipes.add(entry.getKey().getId_equipe());
					}

					for (int i = 0; i < (nbEquipes / 2); i++) {
						cpt++;
						//tirage premier num√É¬©ro
						int indiceEquipe1DansTableau = tirerNombreAleatoire(iDsEquipes);
						//System.out.println("          Indice n√Ç¬∞" + cpt + " tir√É¬© : " + indiceEquipe1DansTableau);
						int idEquipe1 = iDsEquipes.get(indiceEquipe1DansTableau);
						//System.out.println("          idEquipe1 : " + idEquipe1);
						iDsEquipes.remove(indiceEquipe1DansTableau);

						cpt++;
						int indiceEquipe2DansTableau = tirerNombreAleatoire(iDsEquipes);
						//System.out.println("          Indice n√Ç¬∞" + cpt + " tir√É¬© : " + indiceEquipe2DansTableau);
						int idEquipe2 = iDsEquipes.get(indiceEquipe2DansTableau);
						//System.out.println("          idEquipe2 : " + idEquipe2);
						iDsEquipes.remove(indiceEquipe2DansTableau);

						//On creee le match
						//System.out.println("          Creation du  match...");
						Match generatedMatch = new Match(getEquipe(idEquipe1),getEquipe(idEquipe2),1);

						//System.out.println("                    Match " + (i+1) + " : " + getEquipe(idEquipe1).getNom_equipe() + " VS " + getEquipe(idEquipe2).getNom_equipe());
						//System.out.println("Tirage n√Ç¬∞" + (i+1) + " : " + getEquipe(idEquipe1).getNom_equipe() + " VS " + getEquipe(idEquipe2).getNom_equipe());
						liste_matchs.add(generatedMatch);
					}
					afficherListeMatchsGeneres(1);
				}
				else {
					System.out.println("Les tournois en mode eliminatoire ne peuvent comporter que 2, 4 ou 8 equipes. Merci de reverifier les equipes enregistrees.");
				}
			}
			else {
				System.out.println("          Il faut au moins 2 equipes pour realiser un tirage au sort.");
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("<-----  Fin du tirage au sort du rang 1  ----->");
		//genererMatchsTourSuperieurModeEliminatoire(2);
	}

	private int tirerNombreAleatoire(ArrayList<Integer> p_iDsEquipes) {
		Random monRandom = new Random();
		int positionEquipeDansTableauTireeAuSort = monRandom.nextInt(p_iDsEquipes.size());
		return positionEquipeDansTableauTireeAuSort;
	}

	private void genererMatchsModeChampionnat() {

		try {

			System.out.println("on rentre ds genererMatch en mode championnat");
			ArrayList<Equipe> equipesParticipantes = new ArrayList<Equipe>();
			//On met la liste des ≈Ωquipes dans une liste plus simple
			for (Entry<Equipe, Integer> entry: classement.entrySet()) {
				equipesParticipantes.add(entry.getKey());
				System.out.println("  - Equipe " + entry.getKey().getNom_equipe() + " ajoutee.");
			}
			System.out.println("taille de la liste d'equipe : " + equipesParticipantes.size());
			System.out.println("Debut de la generation des matchs");

			for (int i = 0; i < equipesParticipantes.size(); i++) {
				//System.out.println("valeur de i : " + i);
				Equipe equipeI = equipesParticipantes.get(i);
				for (int j = equipesParticipantes.size() - 1; j > 0; j--) {
					//System.out.println("valeur de j : " + j);
					Equipe equipeJ = equipesParticipantes.get(j);
					if (!matchDejaEnregistre(equipeI,equipeJ) && (equipeI != equipeJ)) {
						Match unMatch = new Match(equipeI, equipeJ, 0);
						//Pour le mode championnat, on met 0 ÀÜ tous les matchs comme valeur
						liste_matchs.add(unMatch);
					}
				}
			}

			afficherListeMatchsGeneres(0);
			//genererMatchsTourSuperieurModeEliminatoire(2);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void afficherListeMatchsGeneres(Integer p_rang) {
		if (p_rang == 0) {
			System.out.println("Resultats du tirage au sort des matchs de rang " + p_rang + "...");
		}
		else {
			System.out.println("Resultats du tirage au sort des matchs en mode eliminatoire...");
		}

		int cpt=0;
		for (int i = 0; i < liste_matchs.size(); i ++){
			if (liste_matchs.get(i).getRang() == p_rang) {
				cpt++;
				System.out.println("     Match n°" + cpt + " : " + liste_matchs.get(i).getEquipe1().getNom_equipe() + " VS " + liste_matchs.get(i).getEquipe2().getNom_equipe() + " (Rang " + liste_matchs.get(i).getRang() + ")");
			}
		}
	}

	private Equipe getEquipe(int p_idEquipeRecherchee) {
		Equipe equipeRecherchee = null;
		for (Entry<Equipe, Integer> entry: classement.entrySet() ) {
			if (entry.getKey().getId_equipe()==p_idEquipeRecherchee) {
				equipeRecherchee = entry.getKey();
				break;
			}
		}
		return equipeRecherchee;
	}

	private boolean matchDejaEnregistre(Equipe p_eq1, Equipe p_eq2) {
		boolean res = false;

		for (int i = 0; i < liste_matchs.size(); i ++){
			if ((liste_matchs.get(i).getEquipe1() == p_eq1 && liste_matchs.get(i).getEquipe2() == p_eq2) || (liste_matchs.get(i).getEquipe2() == p_eq1 && liste_matchs.get(i).getEquipe1() == p_eq2)) {
				res = true;
				break;
			}
		}

		return res;
	}

	public String genererMatchsTourSuperieurModeEliminatoire(int p_rangNouveauxMatchs) {
		String res=null;

		if (!isChampionnat()) {
			System.out.println("<-----  Debut du tirage au sort des matchs eliminatoires du rang " + p_rangNouveauxMatchs + " ----->");

			ArrayList<Equipe> listeGagnants = new ArrayList<Equipe>();
			ArrayList<Match> listeMatchsRangInferieur = new ArrayList<Match>();
			try {
				//On v≈Ωrifie que le nombre de matchs pr≈Ωc≈Ωdant le rang plac≈Ω en paramÔøΩtre soit le bon
				int nombreMatchsPrecedentsPourGenererNouveaux = 0;
				for (int i = 0; i < liste_matchs.size(); i ++){
					Match matchEnCours = liste_matchs.get(i);
					int rangDuMatchEnCours = matchEnCours.getRang();

					if (rangDuMatchEnCours == (p_rangNouveauxMatchs - 1)) {
						if (matchEnCours.getScore1() != null && matchEnCours.getScore2() != null) {
							//On v≈Ωrifie que les scores sont bien sup≈Ωrieurs ou ≈Ωgaux ÀÜ 0
							if (matchEnCours.getScore1() >= 0 && matchEnCours.getScore2() >= 0) {
								listeMatchsRangInferieur.add(matchEnCours);
								nombreMatchsPrecedentsPourGenererNouveaux++;
							}
							else {
								System.out.println("Les scores doivent etre superieurs ou egaux a 0 !");
							}
						}
						else {
							System.out.println("Au moins un des deux scores vaut null, on ne traite pas ce match.");
						}
					}
				}

				//Si on a un nombre coh≈Ωrent de matchs correctement renseign≈Ωs
				if (nombreMatchsPrecedentsPourGenererNouveaux %2 == 0) {
					for (int i = 0; i < listeMatchsRangInferieur.size(); i ++){
						Match matchEnCours = listeMatchsRangInferieur.get(i);
						Equipe equipeGagnante = null;

						//On s≈Ωlectionne le gagnant du match 
						if (matchEnCours.getScore1() > matchEnCours.getScore2()) {
							equipeGagnante = matchEnCours.getEquipe1();
						}
						else if (matchEnCours.getScore1() < matchEnCours.getScore2()) {
							equipeGagnante = matchEnCours.getEquipe2();
						}
						else {
							System.out.println("Il ne peut pas y avoir de match nul en mode eliminatoire !");
						}

						//On insÔøΩre le gagnant dans la liste des gagnants
						if (equipeGagnante != null) {
							listeGagnants.add(equipeGagnante);
							//System.out.println("Equipe gagnante du match " + matchEnCours.getEquipe1().getNom_equipe() + " VS " + matchEnCours.getEquipe2().getNom_equipe() + " : " + equipeGagnante.getNom_equipe() + " ajout≈Ωe ÀÜ la liste des gagnants.");
						}
					}
				}
				else if (nombreMatchsPrecedentsPourGenererNouveaux == 1) {
					Match matchEnCours = listeMatchsRangInferieur.get(0);
					Equipe equipeGagnante = null;

					if (matchEnCours.getScore1() > matchEnCours.getScore2()) {
						equipeGagnante = matchEnCours.getEquipe1();
					}
					else if (matchEnCours.getScore1() < matchEnCours.getScore2()) {
						equipeGagnante = matchEnCours.getEquipe2();
					}
					else {
						System.out.println("Il ne peut pas y avoir de match nul en mode eliminatoire !");
					}

					//On insÔøΩre le gagnant dans la liste des gagnants
					if (equipeGagnante != null) {
						listeGagnants.add(equipeGagnante);
						//System.out.println("Equipe gagnante du match " + matchEnCours.getEquipe1().getNom_equipe() + " VS " + matchEnCours.getEquipe2().getNom_equipe() + " : " + equipeGagnante.getNom_equipe() + " ajout≈Ωe ÀÜ la liste des gagnants.");
					}
				}
				else {
					System.out.println("Nombre de matchs n-1 incorrect. Impossible de generer les matchs de rang " + p_rangNouveauxMatchs);
				}

				//Si on a un nombre pair de vainqueurs
				if (listeGagnants.size() %2 == 0) {
					for (int i = 0 ; i< listeGagnants.size() ; i++) {
						//On prend la premiÔøΩre ≈Ωquipe et on lui fait rencontrer celle d'aprÔøΩs
						if (i%2 == 0) {	
							//On teste pour ne pas ÔøΩtre sur le dernier ≈Ωl≈Ωment du tableau
							Match newMatch = new Match(listeGagnants.get(i),listeGagnants.get(i+1), p_rangNouveauxMatchs);
							liste_matchs.add(newMatch);
						}
					}
					afficherListeMatchsGeneres(p_rangNouveauxMatchs);
					System.out.println("<-----  Fin du tirage au sort du rang " + p_rangNouveauxMatchs + "  ----->");
					//genererMatchsTourSuperieurModeEliminatoire(p_rangNouveauxMatchs+1);
				}
				//S'il ne reste qu'un seul participant, c'est que le tournoi est termin≈Ω
				else if (listeGagnants.size() == 1) {
					res = "Felicitations a l'equipe " + listeGagnants.get(0).getNom_equipe() + " qui remporte le tournoi " + this.nom_tournoi;
					System.out.println("            >>>>>>>>  Le gagnant du tournoi " + this.nom_tournoi + " est l'equipe de " + listeGagnants.get(0).getNom_equipe() + "  <<<<<<<");
					System.out.println("<-----  Fin du tirage au sort du rang " + p_rangNouveauxMatchs + "  ----->");
				}
				else {
					System.out.println("Nombre de vainqueurs impair. Veuillez verifier la ArrayList<Equipe> listeGagnants");
					System.out.println("<-----  Fin du tirage au sort du rang " + p_rangNouveauxMatchs + "  ----->");
				}
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		else {
			System.out.println("On ne peut pas generer de matchs aleatoires de rang superieur en mode championnat !");
		}
		return res;

	}

	public void ajouterMatch (Match p_match) {
		if (!liste_matchs.contains(p_match)) {
			liste_matchs.add(p_match);
			System.out.println("Match " + p_match.getEquipe1().getNom_equipe() + " VS " + p_match.getEquipe2().getNom_equipe() + " (Rang " + p_match.getRang() + ") ajout≈Ω.");
		}
	}


	public Match getMatchSpecifique(Equipe equipe1, Equipe equipe2) {
		Match res = null;
		for (int i = 0; i < liste_matchs.size(); i ++){
			//On ne s≈Ωlectionne que les matchs identiques au rang
			Match matchEnCours = liste_matchs.get(i);

			if (matchEnCours.getEquipe1() == equipe1 && matchEnCours.getEquipe2() == equipe2) {
				res = matchEnCours;
			}
		}

		return res;
	}


	public String getTypeTournoi() {
		String res=null;
		if (isModeChampionnat == true) {
			res = "Championnat";
		}
		else {
			res = "Tournoi eliminatoire";
		}
		return res;
	}
	public String afficherInfosEssentielles() {
		StringBuilder sb = new StringBuilder();

		sb.append(this.nom_tournoi);
		sb.append(" (");
		sb.append(getTypeTournoi());
		sb.append(" n°");
		sb.append(this.id_tournoi);
		sb.append(" de ");
		sb.append(this.lib_sport);
		sb.append(" du ");
		sb.append(this.date_creation);
		sb.append(")");

		sb.append("\n\n");
		sb.append("Liste des equipes : " + classement.size());


		for(Entry<Equipe,Integer> entry : classement.entrySet()) {
			sb.append("\n");
			Equipe cle = entry.getKey();
			sb.append("\t  - ");
			sb.append(cle.getNom_equipe());
		}

		sb.append("\n\n");
		sb.append("Liste des matchs : " + liste_matchs.size());

		for (int i=0;i<liste_matchs.size();i++) {
			sb.append("\n");
			sb.append("\t");
			sb.append(liste_matchs.get(i).getEquipe1().getNom_equipe());
			//sb.append("(");
			//sb.append(liste_matchs.get(i).getScore1());
			//sb.append(")");
			sb.append(" VS ");
			sb.append(liste_matchs.get(i).getEquipe2().getNom_equipe());
			//sb.append("(");
			//sb.append(liste_matchs.get(i).getScore2());
			//sb.append(")");
		}
		return sb.toString();	
	}


	public ArrayList<String> verifierGenerationMatchsRangSuperieur() {
		/*
		 * On retourne un tableau avec deux lignes : 
		 *     - la premiere ligne contient les messages d'informations sur la generation de nouveaux matchs
		 *     - la deuxieme ligne contient l'information si les matchs de nouveau rang ont ete generes ou non 
		 */
		ArrayList<String>  arrayToReturn = new ArrayList<String>();
		arrayToReturn.add(0, "");
		arrayToReturn.add(1, "");


		// On ne verifie que si on est en mode eliminatoire
		if (!this.isModeChampionnat) {
			int nbEquipes = this.classement.size();
			int nbMatchsDansRangCourant = 0;
			int rangMax = 0;

			// On cherche le rang max
			for (int i = 0; i < liste_matchs.size(); i++) {
				System.out.println("Rang du match : " + i + " : " + liste_matchs.get(i).getRang());
				if (liste_matchs.get(i).getRang() != null) {
					if(liste_matchs.get(i).getRang() > rangMax) {
						rangMax = liste_matchs.get(i).getRang();
					}
				}
			}

			System.out.println("rang max set to " + rangMax);

			//On ne sélectionne que les matchs qui nous interessent
			List<Match> listeMatchsBonRangNonNulls = new ArrayList<Match>();
			for (int i = 0; i < liste_matchs.size(); i++) {
				if(liste_matchs.get(i).getRang() == rangMax ) {
					nbMatchsDansRangCourant++;
					System.out.println("nbMatchsDansRangCourant : " + nbMatchsDansRangCourant);
					System.out.println("valeur de getScore1 : " + liste_matchs.get(i).getScore1() + " | valeur de getScore2 : " + liste_matchs.get(i).getScore2());
					if (liste_matchs.get(i).getScore1() != null && liste_matchs.get(i).getScore2() != null) {
						listeMatchsBonRangNonNulls.add(liste_matchs.get(i));
					}
				}
			}

			//On regarde si le nombre de matchs eligibles (de m�me rang) est egal au nombre de matchs de m�me rang ET avec des scores non nulls
			if (nbMatchsDansRangCourant == listeMatchsBonRangNonNulls.size()) {
				if ((nbMatchsDansRangCourant/2) != 0) {
					genererMatchsTourSuperieurModeEliminatoire(rangMax+1);
					arrayToReturn.set(0, (nbMatchsDansRangCourant/2) +" nouveau(x) match(s) de rang " + (rangMax+1) + " ajoute(s).");
					arrayToReturn.set(1, "true");
				}
				else {
					arrayToReturn.set(0,genererMatchsTourSuperieurModeEliminatoire(rangMax+1));
				}
			}
			else {
				arrayToReturn.set(0, "Encore " + (nbMatchsDansRangCourant-listeMatchsBonRangNonNulls.size()) + " match(s) a completer pour generer celui(ceux) du rang " + (rangMax + 1));
			}
		}

		return arrayToReturn;
	}

}



